package framework.jackson.xml;

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonSubTypes.Type;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.JsonTypeName;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;

/**
 * 多子类时的序列化: 反序列化成对象时，只需传入父类型，会根据 type 自动选择子类型。<p>
 *     配置方式一：在 @JsonSubTypes({ @Type(value = Sub1.class) }) 中声明具体的子类型，在子类上通过 @JsonTypeName("sub1") 声明具体的子类型名称。
 *     why？很好理解，不指定子类的话 Jackson 不知道使用哪个类型序列化，难道扫描整个目录？<p>
 *     配置方式二：在 @JsonSubTypes({ @Type(value = Sub1.class, name = "sub1") }) 中声明具体的子类型、子类型名称  <p>
 *
 * @author ME
 * @date   2019年12月13日 下午4:48:19
 */
public class TestJacksonXmlSubType {

    public static void main(String[] args) throws Exception {
        XmlMapper xmlMapper = new XmlMapper();

        Sub1 sub1 = new Sub1();
        sub1.setType("sub1");
        sub1.setId(1);
        sub1.setName("ljy");
        String jsonStr1 = xmlMapper.writeValueAsString(sub1);
        System.out.println("sub1 -> xml: " + jsonStr1);

        Sub2 sub2 = new Sub2();
        sub2.setType("sub2");
        sub2.setId(2);
        sub2.setAge(33);
        String jsonStr2 = xmlMapper.writeValueAsString(sub2);
        System.out.println("sub2 -> xml: " + jsonStr2);

        System.out.println("\n反序列化成对象时，默认不丢弃类型标志符，需使用 `@JsonTypeInfo(visible = true)` 开启");
        System.out.println("xml -> sub1: " + xmlMapper.readValue(jsonStr1, MyIn.class).toString());
        System.out.println("xml -> sub2: " + xmlMapper.readValue(jsonStr2, MyIn.class).getClass());
    }

    /**
     * 反序列化时根据 指定的区分属性判断，并踢除（反序列化后属性会不存在）  <p>
     *
     * JsonTypeInfo.As.PROPERTY（默认值）：序列化时，根据配置的属性 另外生成一个属性作为区分标记 <p>
     *     <sub111><id>1</id><type>sub1</type><name>ljy</name></sub111>    <p>
     * JsonTypeInfo.As.EXISTING_PROPERTY：序列化时，会查找指定属性名（不存在就跑错） <p></p>
     */
    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "type", visible = true)
    @JsonSubTypes({
            @Type(value = Sub1.class),
            @Type(value = Sub2.class)
    })
    @JacksonXmlRootElement(localName = "MyIn")
    private static abstract class MyIn {
        private int id;
        private String type;

        public MyIn() {
            super();
        }

        public int getId() {
            return id;
        }

        public void setId(int id) {
            this.id = id;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

    }

    @JsonTypeName("sub1")
    @JacksonXmlRootElement(localName = "sub111")
    private static class Sub1 extends MyIn {
        private String name;

        public Sub1() {}

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Sub1{" + "type='" + super.type + "'}";
        }
    }

    @JsonTypeName("sub2")
    private static class Sub2 extends MyIn {
        private int age;

        public Sub2() {}

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Sub2{" + "type='" + super.type + "'}";
        }

    }

}
